/**
 * \file
 *
 * \brief USB Host Controller (UHC)
 *
 * Copyright (C) 2011 - 2012 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * \page License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 *    Atmel microcontroller product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \asf_license_stop
 *
 */

#include "uhc.h"
#include "uotghs_device.h"
#include "dev_status.h"
#include "gpbr.h"
#include <stdlib.h>
#include <string.h>

#include "io_api.h"   

/*
#ifndef USB_HOST_UHI
#  error USB_HOST_UHI must be defined with unless one UHI interface in conf_usb_host.h file.
#endif
*/

#ifdef USB_HOST_HUB_SUPPORT
#  error The USB HUB support is not available in this revision.
#endif

// Optional UHC callbacks
#ifndef UHC_CONNECTION_EVENT
#  define UHC_CONNECTION_EVENT(dev,b_present)
#endif
#ifndef UHC_DEVICE_CONF
#  define UHC_DEVICE_CONF(dev) 1
#endif
#ifndef UHC_ENUM_EVENT
#  define UHC_ENUM_EVENT(dev,event)
#endif
#ifndef UHC_WAKEUP_EVENT
#  define UHC_WAKEUP_EVENT()
#endif
   
//! Maximum try to enumerate a device
#define UHC_ENUM_NB_TRY 4

//! Entry point of all devices connected on USB tree
#define UHC_USB_ADD_NOT_VALID 0xFF 
   
//! USB address of the USB device under enumeration process
#  define UHC_DEVICE_ENUM_ADD 1 // Static without USB HUB

//! Device under enumeration process

//! Total power of the devices connected
//#  define uhc_power_running   0 // No used without USB HUB  
  
//! Number of UHI available
//#define UHC_NB_UHI  (sizeof(uhc_uhis)/sizeof(uhc_uhis[0]))  
   

/**
 * \ingroup uhc_group
 * \defgroup uhc_group_interne Implementation of UHC
 *
 * Internal implementation 
 * @{
 */

//! \name Internal variables to manage the USB host stack
//! @{   
   
//! Entry point of all devices connected on USB tree
uhc_device_t g_uhc_device_root;
uhc_device_t* uhc_dev_enum  = (&g_uhc_device_root); // Static without USB HUB

//! Number of enumeration try
uint8_t uhc_enum_try;

//! Store the callback to be call at the end of reset signal
uhd_callback_reset_t uhd_reset_callback = NULL;


//! Device under enumeration process
//static uhc_device_t *uhc_dev_enum;

//! Total power of the connected devices
//static uint16_t uhc_power_running;


//! Number of SOF remaining before call uhc_sof_timeout_callback callback


//! Array of all UHI available
//static uhi_api_t uhc_uhis[] = {USB_HOST_UHI};


//! Volatile flag to pool the end of Get USB string setup request
static volatile bool uhc_setup_request_finish;

//! Volatile flag to know the status of Get USB string setup request
static volatile bool uhc_setup_request_finish_status;

//! @}

//static void uhd_ctrl_phase_setup(void);
//static void uhd_ctrl_request_end(uhd_trans_status_t status);

#ifdef SUPPORT_ACCESSORY
static uint8_t g_bulkInEpNum = 0;
static uint8_t g_bulkOutEpNum = 0;
static uint16_t g_bulkPktSize = 0;
static uint8_t g_bulkInPipeNum = 2;
static uint8_t g_bulkOutPipeNum = 3;
static char* g_AoaString[] = 
{
	"Google, Inc."
	,"AccessoryChat"
	,"Accessory Chat"
	,"1.0"
	,"http://www.android.com"
	,"1234567890"
};
static uint8_t g_AoaProtocolVer[2] = {0};
static uint8_t g_AoaStringIndex = 0;
	
#endif

/**
 * \internal
 * \brief Function called by UOTGHS interrupt to manage USB host interrupts
 *
 * USB host interrupt events are split into four sections:
 * - USB line events
 *   (VBus error, device dis/connection, SOF, reset, suspend, resume, wakeup)
 * - control endpoint events
 *   (setup reception, end of data transfer, underflow, overflow, stall, error)
 * - bulk/interrupt/isochronous endpoints events
 *   (end of data transfer, stall, error)
 */
void usb_host_interrupt(void)
{
	uint8_t pipe_int;

	// Manage SOF interrupt
	if (Is_uhd_sof()) {
		uhd_sof_interrupt();
		return;
	}

#if 0
	// Manage pipe interrupts
	pipe_int = uhd_get_interrupt_pipe_number();
	if (pipe_int == 0) {
		// Interrupt acked by control endpoint managed
		uhd_ctrl_interrupt();
		return;
	}
	
#ifdef SUPPORT_ACCESSORY
	if (pipe_int == g_bulkInPipeNum) 
	{
		Pipe_interrupt(pipe_int);
		return;
	}
	else if (pipe_int == g_bulkOutPipeNum)
	{
		Pipe_interrupt(pipe_int);
		return;
	}
#endif  

#else
{
	int i = 0;
	uint32_t status = UOTGHS->UOTGHS_HSTISR;
	uint32_t mask = UOTGHS->UOTGHS_HSTIMR;
	uint32_t pipeIntMask = 0x3FF;
	uint32_t pipeNum = 0;
	
	pipeNum = ((status & mask) >> 8);
	if(pipeNum & pipeIntMask)
	{
		for(i = 0; i < 10; i++, pipeNum >>= 1)
		{
			if(pipeNum & 0x01)
			{
				break;
			}
		}

		pipeNum = i;
		
		if(g_bulkInPipeNum == pipeNum || g_bulkOutPipeNum == pipeNum)
		{
			Pipe_interrupt(pipeNum);
		}
		else if(pipeNum == 0)
		{
			uhd_ctrl_interrupt();
		}
		
		return;
	}
}
     
#endif
        /* // not to use any other ep, %by Santiago
	if (pipe_int != UOTGHS_EPT_NUM) {
		// Interrupt acked by bulk/interrupt/isochronous endpoint
		uhd_pipe_interrupt(pipe_int);
		return;
	}
#ifdef UHD_PIPE_DMA_SUPPORTED
	pipe_int = uhd_get_pipe_dma_interrupt_number();
	if (pipe_int != UOTGHS_EPT_NUM) {
		// Interrupt DMA acked by bulk/interrupt/isochronous endpoint
		uhd_pipe_interrupt_dma(pipe_int);
		return;
	}
#endif*/
	// USB bus reset detection
	if (Is_uhd_reset_sent()) 
        {
                //Clear reset interrupt flag
		UOTGHS->UOTGHS_HSTICR = UOTGHS_HSTICR_RSTIC; //uhd_ack_reset_sent();
		if (uhd_reset_callback != NULL) {
			uhd_reset_callback();
		}
		return;
	}

	// Manage dis/connection event
	if (Is_uhd_disconnection() && Is_uhd_disconnection_int_enabled()) {
		uhd_ack_disconnection();
		uhd_disable_disconnection_int();
		// Stop reset signal, in case of disconnection during reset
		uhd_stop_reset();
                
                //uhd_disable_reset_sent_interrupt();
                //uhd_start_reset();
                uhd_disable(false);
                ex_status.usb_role = USB_ROLE_SWITCH;
                //b_usb_switch = true;                    
                //update_jpm_status(JPM_AS_DEVICE);
                
		// Disable wakeup/resumes interrupts,
		// in case of disconnection during suspend mode
		/*UOTGHS->UOTGHS_HSTIDR = UOTGHS_HSTIDR_HWUPIEC
				| UOTGHS_HSTIDR_RSMEDIEC
				| UOTGHS_HSTIDR_RXRSMIEC;
		uhd_sleep_mode(UHD_STATE_DISCONNECT);
		uhd_ack_connection();
		uhd_enable_connection_int();
		uhd_suspend_start = 0;
		uhd_resume_start = 0;
		uhc_notify_connection(false);*/
		return;
	} //not to care disconnect, %by Santiago
	if (Is_uhd_connection() && Is_uhd_connection_int_enabled()) {
                //Clear connection interrupt flag
		UOTGHS->UOTGHS_HSTICR = UOTGHS_HSTICR_DCONNIC; //uhd_ack_connection();                
                //disable connection intr
                UOTGHS->UOTGHS_HSTIDR = UOTGHS_HSTIDR_DCONNIEC;//uhd_disable_connection_int();
		//uhd_ack_disconnection();
		//uhd_enable_disconnection_int();  //not to care disconnect, %by Santiago
		
                //Start send SOF
                //uhd_enable_sof();
                Set_bits(UOTGHS->UOTGHS_HSTCTRL, UOTGHS_HSTCTRL_SOFE);
                
                // not to use any about suspend\sleep, %by Santiago
		//uhd_sleep_mode(UHD_STATE_IDLE);
		//uhd_suspend_start = 0;
		//uhd_resume_start = 0;
		
                //uhc_notify_connection(true);
                //uhc_connection_tree(true, &g_uhc_device_root);
                uhc_enum_try = 1;
		g_uhc_device_root.conf_desc = NULL;
		g_uhc_device_root.address = 0;
		//UHC_CONNECTION_EVENT(uhc_dev_enum, true);
		uhc_enumeration_step1();
		return;
	}

        //Close all process about VBUS, %by Santiago
        /*
	// Manage Vbus error
	if (Is_uhd_vbus_error_interrupt()) {
		uhd_ack_vbus_error_interrupt();
		//UHC_VBUS_ERROR();
		return;
	}*/	
        
        //not to use sleep\wakeup, %by Santiago
        /*
	if (Is_uhd_wakeup_interrupt_enabled() && (Is_uhd_wakeup() ||
			Is_uhd_downstream_resume() || Is_uhd_upstream_resume())) {
		// Disable wakeup/resumes interrupts
		UOTGHS->UOTGHS_HSTIDR = UOTGHS_HSTIDR_HWUPIEC
				| UOTGHS_HSTIDR_RSMEDIEC
				| UOTGHS_HSTIDR_RXRSMIEC;
		uhd_enable_sof();
		if ((!Is_uhd_downstream_resume())
				&&(!Is_uhd_disconnection())) {
			// It is a upstream resume
			// Note: When the CPU exits from a deep sleep mode, the event
			// Is_uhd_upstream_resume() can be not detected
			// because the USB clock are not available.

			// In High speed mode a downstream resume must be sent
			// after a upstream to avoid a disconnection.
			if (Is_uhd_high_speed_mode()) {
				uhd_send_resume();
			}
		}
		// Wait 50ms before restarting transfer
		uhd_resume_start = 50;
		uhd_sleep_mode(UHD_STATE_IDLE);
		return;
	}
*/
         //Close all process about VBUS, %by Santiago
        
	// Manage Vbus state change
	if (Is_otg_vbus_transition()) {
                // Check USB clock ready after asynchronous interrupt
                while (!Is_otg_clock_usable());
                //otg_unfreeze_clock();
                Clr_bits(UOTGHS->UOTGHS_CTRL, UOTGHS_CTRL_FRZCLK);
                
		otg_ack_vbus_transition();
		return;
	}
/*
	// Other errors
	if (Is_uhd_errors_interrupt_enabled() && Is_uhd_errors_interrupt()) {
		uhd_ack_errors_interrupt();
		return;
	}
	// Still waiting VBus, ignore errors
	if (Is_uhd_vbus_enabled() && !Is_otg_vbus_high()) {
		uhd_ack_errors_interrupt();
		return;
	}*/

	//Assert(false); // Interrupt event no managed, not to use event, %by Santiago
}

/**
 * \brief Enable a internal timeout on SOF events
 *
 * \param timeout  value of timeout (ms)
 * \param callback Callback to call at the end of timeout
 */
void uhc_enable_timeout_callback(uint8_t timeout,
		uhc_sof_timeout_callback_t callback)
{
	uhc_sof_timeout_callback = callback;
	uhc_sof_timeout = timeout;
}

/**
 * \brief Enters a specific device in USB suspend mode
 * Suspend the USB line or a port on USB hub.
 */
/*void uhc_enumeration_suspend(void)
{

	// Suspend USB line
	//uhd_suspend();
}*/

/**
 * \brief Sends the USB Reset signal on the USB line of a device
 *
 * \param callback Callback to call at the end of Reset signal
 */
void uhc_enumeration_reset(uhd_callback_reset_t callback)
{
	// Reset USB line
        uhd_reset_callback = callback;
	uhd_start_reset();
	//uhd_send_reset(callback);
}

/**
 * \brief Manage a device plug or unplug on the USB tree
 *
 * \param b_plug   true, if it is a device connection
 * \param dev      Information about device connected or disconnected
 */
void uhc_connection_tree(bool b_plug, uhc_device_t* dev)
{
	if (b_plug) {
		uhc_enum_try = 1;
		dev->conf_desc = NULL;
		dev->address = 0;
		//UHC_CONNECTION_EVENT(uhc_dev_enum, true);
		uhc_enumeration_step1();
	} 
        else 
        {
		if (uhc_dev_enum == dev) {
			// Eventually stop enumeration timeout on-going on this device
			uhc_sof_timeout = 0;
		}
		// Abort all transfers (endpoint control and other) and free pipe(s)
		//uhd_ep_free(dev->address, 0xFF);
                uhd_disable_pipe(0);
		uhd_unallocate_memory(0);

		// Disable all USB interfaces (this includes HUB interface)
		/*for (uint8_t i = 0; i < UHC_NB_UHI; i++) {
			uhc_uhis[i].uninstall(dev);
		}*/

		//UHC_CONNECTION_EVENT(dev, false);
		dev->address = UHC_USB_ADD_NOT_VALID;
		// Free USB configuration descriptor buffer
		if (dev->conf_desc != NULL) {
			free(dev->conf_desc);
		}
	}
}

/**
 * \brief Device enumeration step 1
 * Reset USB line.
 */
void uhc_enumeration_step1(void)
{
	uhc_enumeration_reset(uhc_enumeration_step2);
}

/**
 * \brief Device enumeration step 2
 * Lets USB line in IDLE state during 20ms.
 */
void uhc_enumeration_step2(void)
{
	uhc_enable_timeout_callback(20, uhc_enumeration_step3);
}

/**
 * \brief Device enumeration step 3
 * Reset USB line.
 */
void uhc_enumeration_step3(void)
{
	uhc_enumeration_reset(uhc_enumeration_step4);
}

/**
 * \brief Device enumeration step 4
 * Lets USB line in IDLE state during 100ms.
 */
void uhc_enumeration_step4(void)
{
	uhc_dev_enum->speed = uhd_get_speed();
	uhc_enable_timeout_callback(100, uhc_enumeration_step5);
}

/**
 * \brief Device enumeration step 5
 * Requests the USB device descriptor.
 * This setup request can be aborted
 * because the control endpoint size is unknown.
 */
void uhc_enumeration_step5(void)
{
	usb_setup_req_t req;

	req.bmRequestType = USB_REQ_RECIP_DEVICE|USB_REQ_TYPE_STANDARD|USB_REQ_DIR_IN;
	req.bRequest = USB_REQ_GET_DESCRIPTOR;
	req.wValue = (USB_DT_DEVICE << 8);
	req.wIndex = 0;
	req.wLength = offsetof(uhc_device_t, dev_desc.bMaxPacketSize0)
			+ sizeof(uhc_dev_enum->dev_desc.bMaxPacketSize0);

	if (!uhd_ep0_alloc(0, 64)) {
		uhc_enumeration_error(UHC_ENUM_HARDWARE_LIMIT);
		return;
	}
	if (!uhd_setup_request(0,
			&req,
			(uint8_t*)&uhc_dev_enum->dev_desc,
			sizeof(usb_dev_desc_t),
			NULL,
			uhc_enumeration_step6)) {
		uhc_enumeration_error(UHC_ENUM_MEMORY_LIMIT);
		return;
	}
}

/**
 * \brief Device enumeration step 6
 * End of Get device descriptor request.
 * Wait 20ms in IDLE state.
 *
 * \param add           USB address of the setup request
 * \param status        Transfer status
 * \param payload_trans Number of data transfered during DATA phase
 */
void uhc_enumeration_step6(
		usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
	UNUSED(add);
	if ((status != UHD_TRANS_NOERROR) || (payload_trans < 8)
			|| (uhc_dev_enum->dev_desc.bDescriptorType != USB_DT_DEVICE)) {
		uhc_enumeration_error((status == UHD_TRANS_DISCONNECT)?
				UHC_ENUM_DISCONNECT:UHC_ENUM_FAIL);
		return;
	}
	// Wait 20ms
	uhc_enable_timeout_callback(20, uhc_enumeration_step7);
}

/**
 * \brief Device enumeration step 7
 * Reset USB line.
 */
void uhc_enumeration_step7(void)
{
	uhc_enumeration_reset(uhc_enumeration_step8);
}

/**
 * \brief Device enumeration step 8
 * Lets USB line in IDLE state during 100ms.
 */
void uhc_enumeration_step8(void)
{
	// Wait 100ms
	uhc_enable_timeout_callback(100, uhc_enumeration_step9);
}

/**
 * \brief Device enumeration step 9
 * Send a Set address setup request.
 */
void uhc_enumeration_step9(void)
{
	usb_setup_req_t req;

	req.bmRequestType = USB_REQ_RECIP_DEVICE
			| USB_REQ_TYPE_STANDARD | USB_REQ_DIR_OUT;
	req.bRequest = USB_REQ_SET_ADDRESS;

	req.wValue = UHC_DEVICE_ENUM_ADD;
	uhc_dev_enum->address = UHC_DEVICE_ENUM_ADD;

	req.wIndex = 0;
	req.wLength = 0;

	// After a USB reset, the reallocation is required
	uhd_ep_free(0, 0);
	if (!uhd_ep0_alloc(0, uhc_dev_enum->dev_desc.bMaxPacketSize0)) {
		uhc_enumeration_error(UHC_ENUM_HARDWARE_LIMIT);
		return;
	}

	if (!uhd_setup_request(0,
			&req,
			(uint8_t*)&uhc_dev_enum->dev_desc,
			sizeof(usb_dev_desc_t),
			NULL,
			uhc_enumeration_step10)) {
		uhc_enumeration_error(UHC_ENUM_MEMORY_LIMIT);
		return;
	}
}

/**
 * \brief Device enumeration step 10
 * End of Set address request.
 * Lets USB line in IDLE state during 20ms.
 *
 * \param add           USB address of the setup request
 * \param status        Transfer status
 * \param payload_trans Number of data transfered during DATA phase
 */
void uhc_enumeration_step10(
		usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
	UNUSED(add);
	UNUSED(payload_trans);
	if (status != UHD_TRANS_NOERROR) {
		uhc_enumeration_error((status == UHD_TRANS_DISCONNECT) ?
				UHC_ENUM_DISCONNECT : UHC_ENUM_FAIL);
		return;
	}
	// Wait 20ms
	uhc_enable_timeout_callback(20, uhc_enumeration_step11);
}

/**
 * \brief Device enumeration step 11
 * Updates USB host pipe with the new USB address.
 * Requests a complete USB device descriptor.
 */
void uhc_enumeration_step11(void)
{
	usb_setup_req_t req;

	// Free address 0 used to start enumeration
	uhd_ep_free(0, 0);

	// Alloc control endpoint with the new USB address
	if (!uhd_ep0_alloc(UHC_DEVICE_ENUM_ADD,
			uhc_dev_enum->dev_desc.bMaxPacketSize0)) {
		uhc_enumeration_error(UHC_ENUM_HARDWARE_LIMIT);
		return;
	}
	// Send USB device descriptor request
	req.bmRequestType = USB_REQ_RECIP_DEVICE|USB_REQ_TYPE_STANDARD|USB_REQ_DIR_IN;
	req.bRequest = USB_REQ_GET_DESCRIPTOR;
	req.wValue = (USB_DT_DEVICE << 8);
	req.wIndex = 0;
	req.wLength = sizeof(usb_dev_desc_t);
	if (!uhd_setup_request(UHC_DEVICE_ENUM_ADD,
			&req,
			(uint8_t *) & uhc_dev_enum->dev_desc,
			sizeof(usb_dev_desc_t),
			NULL, uhc_enumeration_step12)) {
		uhc_enumeration_error(UHC_ENUM_MEMORY_LIMIT);
		return;
	}
}

/**
 * \brief Device enumeration step 12
 * Requests the first USB structure of the USB configuration descriptor.
 *
 * \param add           USB address of the setup request
 * \param status        Transfer status
 * \param payload_trans Number of data transfered during DATA phase
 */
void uhc_enumeration_step12(
		usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
	usb_setup_req_t req;
	uint8_t conf_num;
	UNUSED(add);

	if ((status != UHD_TRANS_NOERROR) || (payload_trans != sizeof(usb_dev_desc_t))
			|| (uhc_dev_enum->dev_desc.bDescriptorType != USB_DT_DEVICE)) {
		uhc_enumeration_error((status==UHD_TRANS_DISCONNECT)?
				UHC_ENUM_DISCONNECT:UHC_ENUM_FAIL);
		return;
	}
	// Choose USB device configuration
        conf_num = 4;
	/*if (uhc_dev_enum->dev_desc.bNumConfigurations > 1) {
		conf_num = UHC_DEVICE_CONF(uhc_dev_enum);
	} else {
		conf_num = 1;
	}*/ //configuration num is 1 at iPhone5, %by Santiago

	uhc_dev_enum->conf_desc = malloc(sizeof(usb_conf_desc_t));
	if (uhc_dev_enum->conf_desc == NULL) {
		uhc_enumeration_error(UHC_ENUM_MEMORY_LIMIT);
		return;
	}
	// Send USB device descriptor request
	req.bmRequestType = USB_REQ_RECIP_DEVICE|USB_REQ_TYPE_STANDARD|USB_REQ_DIR_IN;
	req.bRequest = USB_REQ_GET_DESCRIPTOR;
	req.wValue = (USB_DT_CONFIGURATION << 8) | (conf_num - 1);
	req.wIndex = 0;
	req.wLength = sizeof(usb_conf_desc_t);
	if (!uhd_setup_request(UHC_DEVICE_ENUM_ADD,
			&req,
			(uint8_t *) uhc_dev_enum->conf_desc,
			sizeof(usb_conf_desc_t),
			NULL, uhc_enumeration_step13)) {
		uhc_enumeration_error(UHC_ENUM_MEMORY_LIMIT);
		return;
	}
}

/**
 * \brief Device enumeration step 13
 * Requests a complete Get configuration descriptor.
 *
 * \param add           USB address of the setup request
 * \param status        Transfer status
 * \param payload_trans Number of data transfered during DATA phase
 */
void uhc_enumeration_step13(
		usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
	uint8_t conf_num, conf_size;
	//uint16_t bus_power = 0;
	usb_setup_req_t req;
	UNUSED(add);

	if ((status != UHD_TRANS_NOERROR) || (payload_trans != sizeof(usb_conf_desc_t))
			|| (uhc_dev_enum->conf_desc->bDescriptorType != USB_DT_CONFIGURATION)) {
		uhc_enumeration_error((status == UHD_TRANS_DISCONNECT)?
				UHC_ENUM_DISCONNECT:UHC_ENUM_FAIL);
		return;
	}

        /* // not to check power on uhc, %by Santiago
	if (!(uhc_dev_enum->conf_desc->bmAttributes
			&USB_CONFIG_ATTR_SELF_POWERED)) {
		bus_power = uhc_dev_enum->conf_desc->bMaxPower * 2;
	}

	if ((bus_power + uhc_power_running) > USB_HOST_POWER_MAX) {
		// USB interfaces consumption too high
		//UHC_ENUM_EVENT(uhc_dev_enum, UHC_ENUM_OVERCURRENT);

		// Abort enumeration, set line in suspend mode
		uhc_enumeration_suspend();
		return;
	}*/

	// Save information about USB configuration descriptor size
	conf_size = le16_to_cpu(uhc_dev_enum->conf_desc->wTotalLength);
        if(conf_size == 94)
        {
            b_iOS6 = true;
            //restor_iOS6_flag();
        }
        else
        {
            b_iOS6 = false;
            //clear_iOS6_flag();
        }
	conf_num = uhc_dev_enum->conf_desc->bConfigurationValue;

	// Re alloc USB configuration descriptor
	free(uhc_dev_enum->conf_desc);
	uhc_dev_enum->conf_desc = malloc(conf_size);
	if (uhc_dev_enum->conf_desc == NULL) {
		uhc_enumeration_error(UHC_ENUM_MEMORY_LIMIT);
		return;
	}
	// Send USB device descriptor request
	req.bmRequestType =
			USB_REQ_RECIP_DEVICE | USB_REQ_TYPE_STANDARD |
			USB_REQ_DIR_IN;
	req.bRequest = USB_REQ_GET_DESCRIPTOR;
	req.wValue = (USB_DT_CONFIGURATION << 8) | (conf_num - 1);
	req.wIndex = 0;
	req.wLength = conf_size;
	if (!uhd_setup_request(UHC_DEVICE_ENUM_ADD,
			&req,
			(uint8_t *) uhc_dev_enum->conf_desc,
			conf_size,
			NULL, uhc_enumeration_step14)) {
		uhc_enumeration_error(UHC_ENUM_MEMORY_LIMIT);
		return;
	}
}

/**
 * \brief Device enumeration step 14
 * Enable USB configuration, if unless one USB interface is supported by UHIs.
 *
 * \param add           USB address of the setup request
 * \param status        Transfer status
 * \param payload_trans Number of data transfered during DATA phase
 */
void uhc_enumeration_step14(
		usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
	usb_setup_req_t req;
	//bool b_conf_supported = false;
	UNUSED(add);

#ifdef SUPPORT_OTG
	//uhc_enumeration_SetFeature(3);
	uhc_enumeration_GetOtgDesc(add, status, payload_trans);
	return;
#else

	if ((status != UHD_TRANS_NOERROR)
			|| (payload_trans < sizeof(usb_conf_desc_t))
			|| (uhc_dev_enum->conf_desc->bDescriptorType != USB_DT_CONFIGURATION)
			|| (payload_trans != le16_to_cpu(uhc_dev_enum->conf_desc->wTotalLength))) {
		uhc_enumeration_error((status==UHD_TRANS_DISCONNECT)?
				UHC_ENUM_DISCONNECT:UHC_ENUM_FAIL);
		return;
	}
	// Check if unless one USB interface is supported by UHIs
/*	for (uint8_t i = 0; i < UHC_NB_UHI; i++) {
		switch (uhc_uhis[i].install(uhc_dev_enum)) {
		case UHC_ENUM_SUCCESS:
			b_conf_supported = true;
			break;

		case UHC_ENUM_UNSUPPORTED:
			break;

		default:
			// USB host hardware limitation
			// Free all endpoints
			uhd_ep_free(UHC_DEVICE_ENUM_ADD,0xFF);
			//UHC_ENUM_EVENT(uhc_dev_enum,UHC_ENUM_HARDWARE_LIMIT);

			// Abort enumeration, set line in suspend mode
			uhc_enumeration_suspend();
			return;
		}
	}*/
	/*if (!b_conf_supported) {
		// No USB interface supported
		//UHC_ENUM_EVENT(uhc_dev_enum, UHC_ENUM_UNSUPPORTED);

		// Abort enumeration, set line in suspend mode
		uhc_enumeration_suspend();
		return;
	}*/
	// Enable device configuration
	req.bmRequestType = USB_REQ_RECIP_DEVICE
			| USB_REQ_TYPE_STANDARD | USB_REQ_DIR_OUT;
	req.bRequest = USB_REQ_SET_CONFIGURATION;
	req.wValue = uhc_dev_enum->conf_desc->bConfigurationValue;
	req.wIndex = 0;
	req.wLength = 0;
	if (!uhd_setup_request(UHC_DEVICE_ENUM_ADD,
			&req,
			NULL,
			0,
			NULL, uhc_enumeration_step15
                          )) {
		uhc_enumeration_error(UHC_ENUM_MEMORY_LIMIT);
		return;
	}       
#endif
}

#ifdef SUPPORT_ACCESSORY
extern bool uhd_ep_alloc(usb_add_t add, uint8_t pipeNum, uint8_t epNum, uint8_t ep_size, Bool isIn);

static uint8_t PipeSize_Convert(uint16_t size)
{
	const uint16_t g_PipeSizeMap[][2] = 
	{
		 {PIPE_SIZE_8	, 8}
		,{PIPE_SIZE_16	, 16}
		,{PIPE_SIZE_32	, 32}
		,{PIPE_SIZE_64	, 64}
		,{PIPE_SIZE_128	, 128}
		,{PIPE_SIZE_256	, 256}
		,{PIPE_SIZE_512	, 512}
		,{PIPE_SIZE_1024, 1024}
	};

	uint32_t i = 0;
	for(i = 0; i < sizeof(g_PipeSizeMap) / 4; i++)
	{
		if(g_PipeSizeMap[i][1] == size) 
		{
			return (PipeSize)g_PipeSizeMap[i][0];
		}
	}

	Assert(False);
	return PIPE_MAX;
}

//Enable Bulk In and Bulk Out endpoint
void uhc_enumeration_step15(
		usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
	int i = 0;
	uint8_t* descriptor = (uint8_t*)uhc_dev_enum->conf_desc;
	uint16_t maxSize = uhc_dev_enum->conf_desc->wTotalLength;
	Bool bFlag1 = false;
	Bool bFlag2 = false;
		
	if ((status!=UHD_TRANS_NOERROR) || (payload_trans!=0)) {
		/*for(uint8_t i = 0; i < UHC_NB_UHI; i++) {
			uhc_uhis[i].uninstall(uhc_dev_enum);
		}*/
		uhc_enumeration_error((status == UHD_TRANS_DISCONNECT)?
				UHC_ENUM_DISCONNECT : UHC_ENUM_FAIL);
		return;
	}

    for(i = 0; i < maxSize; i += descriptor[0])
    {
        if (USB_DT_ENDPOINT == descriptor[1])
        {
			if(descriptor[2] & 0x80)
			{
				g_bulkInEpNum = (descriptor[2] & 0x0F);
				g_bulkPktSize = (descriptor[5] << 8) | descriptor[4];
				bFlag1 = true;
			}
			else
			{
				g_bulkOutEpNum = (descriptor[2] & 0x0F);
				bFlag2 = true;
			}
        }

		if(bFlag2 && bFlag1) break;

        descriptor += descriptor[0];
    }
	
	if(!bFlag2 || !bFlag1)
	{
		uhc_enumeration_error((status == UHD_TRANS_DISCONNECT)?
				UHC_ENUM_DISCONNECT : UHC_ENUM_FAIL);
		return;
	}

	if(uhc_dev_enum->dev_desc.idProduct == 0x2D01 || uhc_dev_enum->dev_desc.idProduct == 0x2D00)
	{
		if(!uhd_ep_alloc(UHC_DEVICE_ENUM_ADD, g_bulkInPipeNum, g_bulkInEpNum, PipeSize_Convert(g_bulkPktSize), true))
		{
			uhc_enumeration_error((status == UHD_TRANS_DISCONNECT)?
					UHC_ENUM_DISCONNECT : UHC_ENUM_FAIL);
			return;
		}
		if(!uhd_ep_alloc(UHC_DEVICE_ENUM_ADD, g_bulkOutPipeNum, g_bulkOutEpNum, PipeSize_Convert(g_bulkPktSize), false))
		{
			uhc_enumeration_error((status == UHD_TRANS_DISCONNECT)?
					UHC_ENUM_DISCONNECT : UHC_ENUM_FAIL);
			return;
		}
#if 1
		if(Is_uhd_pipe_configured(g_bulkInPipeNum))
		{
			uhd_configure_pipe_token(g_bulkInPipeNum, UOTGHS_HSTPIPCFG_PTOKEN_IN);
			uhd_ack_in_received(g_bulkInPipeNum);
			uhd_enable_continuous_in_mode(g_bulkInPipeNum);

			bFlag2 = Is_uhd_continuous_in_mode_enabled(g_bulkInPipeNum);
			uhd_enable_in_received_interrupt(g_bulkInPipeNum);
			uhd_ack_fifocon(g_bulkInPipeNum);
			uhd_unfreeze_pipe(g_bulkInPipeNum);
		}
#else	
		if(Is_uhd_pipe_configured(g_bulkOutPipeNum))
		{
			int i = 0;
			volatile uint8_t* ptr_ep_data;
			
			uhd_configure_pipe_token(g_bulkOutPipeNum, UOTGHS_HSTPIPCFG_PTOKEN_OUT);
			uhd_ack_out_ready(g_bulkOutPipeNum);
			
			ptr_ep_data = (volatile uint8_t *)&uhd_get_pipe_fifo_access(g_bulkOutPipeNum, 8);

			for(i = 0; i < 10; i++)
			{
				*ptr_ep_data++ = i + '0';
			}

			uhd_enable_out_ready_interrupt(g_bulkOutPipeNum);
			uhd_ack_fifocon(g_bulkOutPipeNum);
			uhd_unfreeze_pipe(g_bulkOutPipeNum);
		}
#endif		
	}
	else
	{
		g_AoaStringIndex = 0;
		uhc_enumeration_GetProtocol(add);
	}
}
#else
/**
 * \brief Device enumeration step 15
 * Enables UHI interfaces
 *
 * \param add           USB address of the setup request
 * \param status        Transfer status
 * \param payload_trans Number of data transfered during DATA phase
 */
void uhc_enumeration_step15(
		usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
	UNUSED(add);
        usb_setup_req_t req;
	if ((status!=UHD_TRANS_NOERROR) || (payload_trans!=0)) {
		/*for(uint8_t i = 0; i < UHC_NB_UHI; i++) {
			uhc_uhis[i].uninstall(uhc_dev_enum);
		}*/
		uhc_enumeration_error((status == UHD_TRANS_DISCONNECT)?
				UHC_ENUM_DISCONNECT : UHC_ENUM_FAIL);
		return;
	}

	// Enable all UHIs supported
	/*for (uint8_t i = 0; i < UHC_NB_UHI; i++) {
		uhc_uhis[i].enable(uhc_dev_enum);
	}*/
	uhc_enum_try = 0;
	
	//UHC_ENUM_EVENT(uhc_dev_enum, UHC_ENUM_SUCCESS);
        req.bmRequestType = USB_REQ_RECIP_DEVICE
			| USB_REQ_TYPE_VENDOR | USB_REQ_DIR_OUT;
        
        req.bRequest = USB_REQ_ROLE_SWITCH;
        req.wValue = 0;
	req.wIndex = 0;
	req.wLength = 0;
        
	if (!uhd_setup_request(UHC_DEVICE_ENUM_ADD,
			&req,
			NULL,
			0,
			NULL, uhc_enumeration_step16)) 
        {
		uhc_enumeration_error(UHC_ENUM_MEMORY_LIMIT);
		return;
	}
}
#endif

void uhc_enumeration_step16(
		usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
        UNUSED(add);
        if ((status!=UHD_TRANS_NOERROR) || (payload_trans!=0)) 
        {
	
		uhc_enumeration_error((status == UHD_TRANS_DISCONNECT)?
				UHC_ENUM_DISCONNECT : UHC_ENUM_FAIL);
		return;
	}
        
        uhd_enable_disconnection_int();        
}

/**
 * \brief Manage error during device enumeration
 *
 * \param status        Enumeration error occurred
 */
void uhc_enumeration_error(uhc_enum_status_t status)
{
	if (status == UHC_ENUM_DISCONNECT) {
		uhc_enum_try = 0;
		return; // Abort enumeration process
	}
	uhd_ep_free(uhc_dev_enum->address, 0xFF);

	// Free USB configuration descriptor buffer
	if (uhc_dev_enum->conf_desc != NULL) {
		free(uhc_dev_enum->conf_desc);
		uhc_dev_enum->conf_desc = NULL;
	}
	uhc_dev_enum->address = 0;
	if (uhc_enum_try++ < UHC_ENUM_NB_TRY) {
		// Restart enumeration at beginning
		uhc_enumeration_step1();
		return;
	}
	// Abort enumeration, set line in suspend mode
	//uhc_enumeration_suspend();
	//UHC_ENUM_EVENT(uhc_dev_enum, status);
	uhc_enum_try = 0;
}

/**
 * \brief Enables or disables the remote wakeup feature
 * of all devices connected
 *
 * \param b_enable   true to enable remote wakeup feature, else disable.
 */
/*static void uhc_remotewakeup(bool b_enable)
{
	usb_setup_req_t req;
	uhc_device_t *dev;

	dev = &g_uhc_device_root;
	while(1) {
		if (dev->conf_desc->bmAttributes & USB_CONFIG_ATTR_REMOTE_WAKEUP) {
			if (b_enable) {
				req.bRequest = USB_REQ_SET_FEATURE;
			} else {
				req.bRequest = USB_REQ_CLEAR_FEATURE;
			}
			req.bmRequestType = USB_REQ_RECIP_DEVICE
					|USB_REQ_TYPE_STANDARD|USB_REQ_DIR_OUT;
			req.wValue = USB_DEV_FEATURE_REMOTE_WAKEUP;
			req.wIndex = 0;
			req.wLength = 0;
			uhd_setup_request(dev->address,&req,NULL,0,NULL,NULL);
		}
#ifdef USB_HOST_HUB_SUPPORT
		if (dev->next == NULL) {
			break;
		}
		dev = dev->next;
#else
		break;
#endif
	}
}*/

/**
 * \brief Callback used to signal the end of a setup request
 *
 * \param add           USB address of the setup request
 * \param status        Transfer status
 * \param payload_trans Number of data transfered during DATA phase
 */
/*void uhc_setup_request_callback(
		usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
	UNUSED(add);
	UNUSED(payload_trans);
	uhc_setup_request_finish_status = (status == UHD_TRANS_NOERROR);
	uhc_setup_request_finish = true;
}*/


/**
 * \name Callbacks used by USB Host Driver (UHD) to notify events
 * @{
 */
/*void uhc_notify_connection(bool b_plug)
{
	if (b_plug) {
		//Assert(g_uhc_device_root.address == UHC_USB_ADD_NOT_VALID);

#ifdef USB_HOST_HUB_SUPPORT
		uhc_power_running = 0;
#endif
	} else {
		if (g_uhc_device_root.address == UHC_USB_ADD_NOT_VALID) {
			// Already disconnected
			// Ignore the noise from host stop process
			return;
		}
	}
	// Device connection on root
	uhc_connection_tree(b_plug, &g_uhc_device_root);
}*/

/*void uhc_notify_sof(bool b_micro)
{
	// Call all UHIs
	for (uint8_t i = 0; i < UHC_NB_UHI; i++) {
		if (uhc_uhis[i].sof_notify != NULL) {
			uhc_uhis[i].sof_notify(b_micro);
		}
	}

	if (!b_micro) {
		// Manage SOF timeout
		if (uhc_sof_timeout) {
			if (--uhc_sof_timeout == 0) {
				uhc_sof_timeout_callback();
			}
		}
	}
}*/

/*void uhc_notify_resume(void)
{
	uhc_remotewakeup(false);
//	UHC_WAKEUP_EVENT();
}*/

//! @}


/**
 * \name Functions to control the USB host stack
 *
 * @{
 */
/*void uhc_start(void)
{
	//g_uhc_device_root.address = UHC_USB_ADD_NOT_VALID;
	uhc_sof_timeout = 0; // No callback registered on a SOF timeout
	uhd_enable();
}
*/
/*void uhc_stop(bool b_id_stop)
{
	// Stop UHD
	uhd_disable(b_id_stop);
}*/

/*void uhc_suspend(bool b_remotewakeup)
{
	if (uhc_enum_try) {
		// enumeration on-going, the USB suspend can't be done
		return;
	}

	if (b_remotewakeup) {
		uhc_remotewakeup(true);
	}
	// Suspend all USB devices
	uhd_suspend();
}*/

/*bool uhc_is_suspend(void)
{
	if (g_uhc_device_root.address == UHC_USB_ADD_NOT_VALID) {
		return true;
	}
	return uhd_is_suspend();
}*/

/*void uhc_resume(void)
{
	if (!uhc_is_suspend()) {
		return;
	}
	// Resume all USB devices
	uhd_resume();
}*/

//! @}


/**
 * \name User functions to manage the devices
 *
 * @{
 */
/*uint8_t uhc_get_device_number(void)
{
#ifdef USB_HOST_HUB_SUPPORT
	uint8_t nb_dev = 0;
	uhc_device_t *dev;

	if (g_uhc_device_root.address != UHC_USB_ADD_NOT_VALID) {
		dev = &g_uhc_device_root;
		while (nb_dev++) {
			if (dev->next == NULL) {
				break;
			}
			dev = dev->next;
		}
	}
	return nb_dev;
#else
	return (g_uhc_device_root.address != UHC_USB_ADD_NOT_VALID) ? 1 : 0;
#endif
}*/

/*char *uhc_dev_get_string_manufacturer(uhc_device_t * dev)
{
	if (!dev->dev_desc.iManufacturer) {
		return NULL; // No manufacturer string available
	}
	return uhc_dev_get_string(dev, dev->dev_desc.iManufacturer);
}*/

/*char *uhc_dev_get_string_product(uhc_device_t * dev)
{
	if (!dev->dev_desc.iProduct) {
		return NULL; // No product string available
	}
	return uhc_dev_get_string(dev, dev->dev_desc.iProduct);
}*/

/*char *uhc_dev_get_string_serial(uhc_device_t * dev)
{
	if (!dev->dev_desc.iSerialNumber) {
		return NULL; // No serial string available
	}
	return uhc_dev_get_string(dev, dev->dev_desc.iSerialNumber);
}*/

/*char *uhc_dev_get_string(uhc_device_t * dev, uint8_t str_id)
{
	usb_setup_req_t req;
	usb_str_desc_t str_header;
	usb_str_lgid_desc_t *str_desc;
	char *string;
	uint8_t i;
	UNUSED(dev);

	req.bmRequestType = USB_REQ_RECIP_DEVICE|USB_REQ_TYPE_STANDARD|USB_REQ_DIR_IN;
	req.bRequest = USB_REQ_GET_DESCRIPTOR;
	req.wValue = (USB_DT_STRING << 8) | str_id;
	req.wIndex = 0;
	req.wLength = sizeof(usb_str_desc_t);

	// Get the size of string
	uhc_setup_request_finish = false;
	if (!uhd_setup_request(0,
			&req,
			(uint8_t*)&str_header,
			sizeof(usb_str_desc_t),
			NULL,
			uhc_setup_request_callback)) {
		return NULL;
	}
	while (!uhc_setup_request_finish);
	if (!uhc_setup_request_finish_status) {
		return NULL;
	}
	// Get the size of string
	str_desc = malloc(str_header.bLength);
	if (str_desc == NULL) {
		return NULL;
	}
	req.wLength = str_header.bLength;
	uhc_setup_request_finish = false;
	if (!uhd_setup_request(0,
			&req,
			(uint8_t*)str_desc,
			str_header.bLength,
			NULL,
			uhc_setup_request_callback)) {
		return NULL;
	}
	while (!uhc_setup_request_finish);
	if (!uhc_setup_request_finish_status) {
		free(str_desc);
		return NULL;
	}
	// The USB strings are "always" in ASCII format, then translate it.
	str_header.bLength = (str_header.bLength - 2) / 2; // Number of character
	string = malloc(str_header.bLength + 1); // +1 for NULL terminal
	if (string == NULL) {
		free(str_desc);
		return NULL;
	}
	for (i = 0; i < str_header.bLength; i++) {
		string[i] = le16_to_cpu(str_desc->string[i]) & 0xFF;
	}
	string[i] = 0;
	free(str_desc);

	return string;
}*/

/*uint16_t uhc_dev_get_power(uhc_device_t* dev)
{
	return dev->conf_desc->bMaxPower * 2;
}*/

/*uhd_speed_t uhc_dev_get_speed(uhc_device_t * dev)
{
	return dev->speed;
}*/

/*bool uhc_dev_is_high_speed_support(uhc_device_t* dev)
{
	usb_setup_req_t req;
	usb_dev_qual_desc_t qualifier;

	if (dev->speed == UHD_SPEED_HIGH) {
		return true;
	}
	if (dev->speed == UHD_SPEED_FULL) {
		req.bmRequestType = USB_REQ_RECIP_DEVICE
				| USB_REQ_TYPE_STANDARD | USB_REQ_DIR_IN;
		req.bRequest = USB_REQ_GET_DESCRIPTOR;
		req.wValue = (USB_DT_DEVICE_QUALIFIER << 8);
		req.wIndex = 0;
		req.wLength = sizeof(qualifier);

		// Get the size of string
		uhc_setup_request_finish = false;
		if (!uhd_setup_request(0,
				&req,
				(uint8_t*)&qualifier,
				sizeof(qualifier),
				NULL,
				uhc_setup_request_callback)) {
			return NULL;
		}
		while (!uhc_setup_request_finish);
		return uhc_setup_request_finish_status;
	}
	return false; // Low speed device
}*/

//! @}

//! @}

#ifdef SUPPORT_OTG

static void uhc_enumeration_SetFeatureDone(usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
        UNUSED(add);
        if ((status!=UHD_TRANS_NOERROR) || (payload_trans!=0)) 
        {
		uhc_enumeration_error((status == UHD_TRANS_DISCONNECT)?
				UHC_ENUM_DISCONNECT : UHC_ENUM_FAIL);
		return;
		}
        
        uhd_enable_disconnection_int();        
}

void uhc_enumeration_SetFeature(uint8_t nFeatureSelector)
{
	usb_setup_req_t req;

	req.bmRequestType = USB_REQ_RECIP_DEVICE | USB_REQ_TYPE_STANDARD | USB_REQ_DIR_OUT;
	req.bRequest = USB_REQ_SET_FEATURE;

	req.wValue = nFeatureSelector;	//b_hnp_enable

	req.wIndex = 0;
	req.wLength = 0;

	//uhd_ep_free(0, 0);
	if (!uhd_ep0_alloc(0, uhc_dev_enum->dev_desc.bMaxPacketSize0)) {
		uhc_enumeration_error(UHC_ENUM_HARDWARE_LIMIT);
		return;
	}

	if (!uhd_setup_request(0,
			&req,
			NULL,
			0,
			NULL,
			uhc_enumeration_SetFeatureDone)) {
		uhc_enumeration_error(UHC_ENUM_MEMORY_LIMIT);
		return;
	}
}

static usb_otg_desc_t g_otgDesc = {0};
static void uhc_enumeration_GetOtgDescDone(
		usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
	//usb_setup_req_t req;
	//uint8_t conf_num;
	UNUSED(add);

	uhc_enumeration_SetFeature(3);
/*
	if ((status != UHD_TRANS_NOERROR) || (payload_trans != sizeof(usb_otg_desc_t))
			|| (g_otgDesc.bDescriptorType != USB_DT_OTG)) {
		uhc_enumeration_error((status==UHD_TRANS_DISCONNECT)?
				UHC_ENUM_DISCONNECT:UHC_ENUM_FAIL);
		return;
	}

	if(g_otgDesc.bmAttributes & 0x03)
	{
		//Support HNP and SRP
		uhc_enumeration_SetFeature(3);
	}
	else
	{
		//Peer don't suport HNP and SRP
	}
	*/
}

void uhc_enumeration_GetOtgDesc(usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
	usb_setup_req_t req;
	
	UNUSED(add);
	
	if (status!=UHD_TRANS_NOERROR) {
		uhc_enumeration_error((status == UHD_TRANS_DISCONNECT)?
				UHC_ENUM_DISCONNECT : UHC_ENUM_FAIL);
		return;
	}

	// Alloc control endpoint with the new USB address
	if (!uhd_ep0_alloc(UHC_DEVICE_ENUM_ADD,
			uhc_dev_enum->dev_desc.bMaxPacketSize0)) {
		uhc_enumeration_error(UHC_ENUM_HARDWARE_LIMIT);
		return;
	}
	// Send USB device descriptor request
	req.bmRequestType = USB_REQ_RECIP_DEVICE|USB_REQ_TYPE_STANDARD|USB_REQ_DIR_IN;
	req.bRequest = USB_REQ_GET_DESCRIPTOR;
	req.wValue = (USB_DT_OTG << 8);
	req.wIndex = 0;
	req.wLength = sizeof(usb_otg_desc_t);
	if (!uhd_setup_request(UHC_DEVICE_ENUM_ADD,
			&req,
			(uint8_t *)& g_otgDesc,
			sizeof(usb_otg_desc_t),
			NULL, uhc_enumeration_GetOtgDescDone)) {
		uhc_enumeration_error(UHC_ENUM_MEMORY_LIMIT);
		return;
	}
}
#endif

#ifdef SUPPORT_ACCESSORY

static void uhc_SendReq(usb_add_t add
	, uint8_t bmRequestType
	, uint8_t bRequest
	, uint16_t wValue
	, uint16_t wIndex
	, uint16_t wLength
	, const void* pRspBuf
	, uhd_callback_setup_end_t ReqDone
	)
{
	usb_setup_req_t req;
        UNUSED(add);

	req.bmRequestType = bmRequestType;
	req.bRequest = bRequest;

	req.wValue = wValue;

	req.wIndex = wIndex;
	req.wLength = wLength;

	if (!uhd_setup_request(UHC_DEVICE_ENUM_ADD,
			&req,
			(uint8_t *)pRspBuf,
			wLength,
			NULL, ReqDone)) 
	{
		uhc_enumeration_error(UHC_ENUM_MEMORY_LIMIT);
		return;
	}
}

static void uhc_enumeration_ReqStartUpAccessoryModeDone(
		usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
	UNUSED(add);
	UNUSED(payload_trans);

	uhd_enable();
	uhc_enumeration_error(UHC_ENUM_MEMORY_LIMIT);
	/*
	if (status!=UHD_TRANS_NOERROR) 
	{
		g_AoaStringIndex = 0;
		uhc_enumeration_error((status == UHD_TRANS_DISCONNECT)?
				UHC_ENUM_DISCONNECT : UHC_ENUM_FAIL);
		return;
	}
	*/
}


static void uhc_enumeration_GetProtocolDone(
		usb_add_t add,
		uhd_trans_status_t status,
		uint16_t payload_trans)
{
#define STRID_MANUFACTURER_NAME 0
#define STRID_MODEL_NAME 		1
#define STRID_DESCRIPTION 		2
#define STRID_VERSION 			3
#define STRID_URL 				4
#define STRID_SERIAL_NUMBER 	6

	UNUSED(add);
	UNUSED(payload_trans);
	
	
	if (status!=UHD_TRANS_NOERROR) 
	{
		g_AoaStringIndex = 0;
		uhc_enumeration_error((status == UHD_TRANS_DISCONNECT)?
				UHC_ENUM_DISCONNECT : UHC_ENUM_FAIL);
		return;
	}

	if(g_AoaProtocolVer[0] >= 1)
	{
		if(g_AoaStringIndex+1 <= STRID_SERIAL_NUMBER)
		{
			//send identifying string information to the device
			uhc_SendReq(add, USB_REQ_RECIP_DEVICE|USB_REQ_TYPE_VENDOR|USB_REQ_DIR_OUT
				, 52	//identifying string information
				, 0	//
				, g_AoaStringIndex	//string ID
				, strlen(g_AoaString[g_AoaStringIndex])+1
				, g_AoaString[g_AoaStringIndex]
				, (uhd_callback_setup_end_t)uhc_enumeration_GetProtocolDone
				);
			g_AoaStringIndex++;
		}
		else	//Set all identifying string information done.
		{
			//start up in accessory mode
			uhc_SendReq(add, USB_REQ_RECIP_DEVICE|USB_REQ_TYPE_VENDOR|USB_REQ_DIR_OUT
				, 53	//request the device start up in accessory mode
				, 0	//
				, g_AoaStringIndex	//string ID
				, 0
				, NULL
				, (uhd_callback_setup_end_t)uhc_enumeration_ReqStartUpAccessoryModeDone
				);
		}
	}
	else
	{
	}
}

void uhc_enumeration_GetProtocol(usb_add_t add)
{
	uhc_SendReq(add, USB_REQ_RECIP_DEVICE|USB_REQ_TYPE_VENDOR|USB_REQ_DIR_IN
		, 51	//Get Protocol
		, 0
		, 0
		, sizeof(g_AoaProtocolVer)
		, &g_AoaProtocolVer
		, (uhd_callback_setup_end_t)uhc_enumeration_GetProtocolDone
		);

}

void Pipe_interrupt(uint8_t pipeNum)
{
	uint8_t nb_byte_received;
	uint8_t *ptr_ep_data;
	int i = 0;
	uint8_t g_buff[10];
	// Disable setup, IN and OUT interrupts of control endpoint
	//UOTGHS->UOTGHS_HSTPIPIDR[pipeNum] = UOTGHS_HSTPIPIDR_RXINEC | UOTGHS_HSTPIPIDR_TXOUTEC;
	if (Is_uhd_in_received(pipeNum)) 
	{
		UOTGHS->UOTGHS_HSTPIPIDR[pipeNum] = UOTGHS_HSTPIPIDR_RXINEC;
		nb_byte_received = uhd_byte_count(pipeNum);
		ptr_ep_data = (uint8_t *) & uhd_get_pipe_fifo_access(pipeNum, 8);

		for(i = 0; i < nb_byte_received; i++)
		{
			g_buff[i] = *ptr_ep_data++;
		}


		UOTGHS->UOTGHS_HSTPIPICR[pipeNum] = UOTGHS_HSTPIPICR_RXINIC;
		
		uhd_enable_in_received_interrupt(pipeNum);
		uhd_ack_fifocon(pipeNum);
		uhd_unfreeze_pipe(pipeNum);
		return;
	}
	else if(Is_uhd_out_ready(pipeNum))
	{
		uhd_freeze_pipe(pipeNum);
		uhd_ack_out_ready(pipeNum);
	}
}
#endif


